#pragma once

#include "../Trigonometric/TrigonometricBernsteinSurfaces3.h"
#include "../Core/Matrices.h"
#include "../Core/TriangulatedMeshes3.h"
#include "../Core/Exceptions.h"
#include <stdlib.h>

namespace cagd {
    class TrigoBernsteinSurfPopulation {
    public:
        class Individual {
            friend class TrigoBernsteinSurfPopulation;
        protected:

            GLdouble    _alpha;
            GLuint      _n;
            GLdouble    _beta;
            GLuint      _m;
            Matrix<DCoordinate3>            _data_net_to_interpolate;
            RowMatrix<GLdouble>             _u_knot_vector;
            ColumnMatrix<GLdouble>          _v_knot_vector;
//            GLuint                          _max_order_of_derivatives;
            GLuint                          _u_div_point_count;
            GLuint                          _v_div_point_count;

            TrigonometricBernsteinSurface3  *_individual;

            GLdouble            _fitness;
            TriangulatedMesh3   *_image;

        public:
            Individual(GLdouble alpha, GLuint n, GLdouble beta, GLuint m,
                       Matrix<DCoordinate3> data_net, RowMatrix<GLdouble> u_knot, ColumnMatrix<GLdouble> v_knot,
                       GLuint u_div_point = 100, GLuint v_div_point = 100);
            Individual(GLdouble alpha, GLuint n, GLdouble beta, GLuint m,
                       Matrix<DCoordinate3> data_net,
                       GLuint u_div_point = 100, GLuint v_div_point = 100);

            GLboolean randomizeUKnots(GLuint number_of_knots);
            GLboolean randomizeVKnots(GLuint number_of_knots);
            GLboolean calculateInterpolation();
            GLboolean calculateLength();
            GLvoid render();
            GLdouble getFitness() {
                return _fitness;
            }

            TriangulatedMesh3* getImage() {
                return _image;
            }

            TrigonometricBernsteinSurface3* getSurface() {
                return _individual;
            }

            ~Individual() {
                if (_individual) {
                    delete _individual; _individual = 0;
                }
                if (_image) {
                    delete _image; _image = 0;
                }
            }
        };

    protected:
        GLdouble    _alpha;
        GLuint      _n;
        GLdouble    _beta;
        GLuint      _m;

        Matrix<DCoordinate3>            _data_net_to_interpolate;

        GLuint _number_of_individuals;
        RowMatrix<Individual*> _individuals;

        GLuint _iterationBetweenGeneration;

        GLdouble r_max;

//        RowMatrix<GLuint> _selection;
//        GLuint _selection_size;

    public:

        TrigoBernsteinSurfPopulation(GLuint number_of_individuals,
                                     GLdouble alpha, GLuint n, GLdouble beta, GLuint m,
                                     Matrix<DCoordinate3> data_net,
                                     GLuint iterationBetweenGeneration = 1,
                                     GLuint u_div_point = 100, GLuint v_div_point = 100);

        Individual* getBest();
        RowMatrix<Individual*> getAllIndividual();

        GLvoid sort();
        RowMatrix<GLdouble> sortUKnots(RowMatrix<GLdouble> knots_vector);
        ColumnMatrix<GLdouble> sortVKnots(ColumnMatrix<GLdouble> knots_vector);

        Individual* selection(RowMatrix<Individual*> _individuals);
        GLboolean mutation();
        GLboolean recombination();
        GLvoid searchPlace(Individual* first_parent, Individual* second_parent, Individual* child);
        GLvoid evolve();

        ~TrigoBernsteinSurfPopulation() {
            cout << "destructor Pop\n";
            for (GLuint i = 0; i < _individuals.GetColumnCount(); i++) {
                if (_individuals[i]) {
                    delete _individuals[i]; _individuals[i] = 0;
                }
            }
        }
    };


}
